
import time
from fractions import Fraction

class Event:
    type = "AbstractEvent"

class ActEvent(Event):
    def __init__(self, tag):
        self.type = "Act"
        self.tag = tag

    def __repr__(self):
        return f"<Act {self.tag}>"


class EnvMesEvent(Event):
    def __init__(self, tag):
        self.type = "Env"
        self.tag = tag

    def __repr__(self):
        return f"<EnvMes {self.tag}>"

def popall(l):
    l2 = l.copy()
    l.clear()
    return l2

class Frame:
    def __init__(self):
        # 控件可以随时调整回调列表，注册回调和移除回调
        #本设计无法隔离控件的相互作用。但耦合往往不得不存在
        self.env_callback = []
        self.act_callback = []
        self.update_callback = []

        self.__event_queue = []
        self.__dt = Fraction(1, 20)
        self.__running_time = Fraction(0)

    @property
    def running_time(self):
        return round(float(self.__running_time), 4)

    def post_event(self, e):
        self.__event_queue.append(e)

    def dispatch(self, e):
        if e.type == "Act":
            print(' ' * 5 + "行动:", e)
            for callback in self.act_callback:
                r = callback(self, e)
                if r == True:
                    break
        elif e.type == "Env":
            for callback in self.env_callback:
                r = callback(self, e)
                if r == True:
                    break

    def update(self, dt):
        # ~ print("tick", self.running_time)
        for callback in self.update_callback:
            callback(self, dt)

    def mainloop(self, keep=5, speed=1):
        running = True
        while running:
            for e in popall(self.__event_queue):
                self.dispatch(e)
            self.update(float(self.__dt))
            time.sleep(float(self.__dt)/speed)

            self.__running_time += self.__dt
            if self.running_time > keep:
                running = False



class Passer:
    def __init__(self, t):
        self.t = t
        self.acc = 0
        self.f = None
        self.f_end = None

    def bindf(self, f):
        assert callable(f)
        self.f = f

    def bindend(self, f):
        assert callable(f)
        self.f_end = f

    def update(self, frame, dt):
        self.acc += dt
        if self.acc > self.t:
            # ~ print("Passer end")
            if self.f_end is not None:
                self.f_end()
                self.unreg_event(frame)
        else:
            # ~ print("Passer")
            if self.f is not None:
                self.f(self.acc / self.t)

    def reg_event(self, frame):
        frame.update_callback.append(self.update)

    def unreg_event(self, frame):
        frame.update_callback.remove(self.update)


class Timer:
    def __init__(self, t):
        self.t = t
        self.acc = 0
        self.f = None

    def bind(self, f):
        assert callable(f)
        self.f = f

    def update(self, frame, dt):
        self.acc += dt
        if self.acc > self.t:
            # ~ print("Timer tick")
            if self.f is not None:
                self.f()
                self.acc = 0

    def reg_event(self, frame):
        frame.update_callback.append(self.update)

    def unreg_event(self, frame):
        frame.update_callback.remove(self.update)




